#ifndef ENTITYSCENENODE_H
#define ENTITYSCENENODE_H
#include <QString>
#include "Vector3.h"
#include "EntityPhysical.h"
#include "EntityMaterial.h"
#include "../Program.h"

/**
 * @brief 场景节点
 */
class EntitySceneNode
{
public:
    // 父节点
    EntitySceneNode* Parent;
    // 节点类型
    QString Type;
    // 节点名称
    QString Name;
    // 节点属性
    QString Attribute;

    // 位置
    Vector3 Position;
    // 角度
    Vector3 Angle;
    // 缩放
    Vector3 Scale;
    // 可见性
    bool Visible;
    // 物理绑定参数
    EntityPhysical Physical;
    // PBR 材质
    EntityMaterial Material;
    // 附加脚本
    QStringList ExtraScript;

    /**
     * @brief 节点插入脚本
     * @return
     */
    QString ScriptInsert() const
    {
        QString p = Parent == nullptr ? "nil" : "'" + Parent->ToString() + "'";
        if (Attribute.isEmpty()) return QString("scene.insert(%1,'%2','%3')").arg(p, Type, Name);
        else return QString("scene.insert(%1,'%2','%3',{%4})").arg(p, Type, Name, Attribute).replace('\\', '/');
    }

    /**
     * @brief 节点变换脚本
     * @return
     */
    QString ScriptTransform() const
    {
        const QString format = "scene.%1('%2',%3)";
        QString command = QString(format).arg("position", Name, Position.ToString());
        if (Angle.x() != 0.0f || Angle.y() != 0.0f || Angle.z() != 0.0f) command += "\n" + QString(format).arg("angle", Name, Angle.ToString());
        if (Scale.x() != 1.0f || Scale.y() != 1.0f || Scale.z() != 1.0f) command += "\n" + QString(format).arg("scale", Name, Scale.ToString());
        command += "\n" + QString("scene.visible('%1',%2)").arg(Name, Visible ? "true" : "false");
        return command;
    }

    /**
     * @brief 渲染模式脚本
     * @return
     */
    QString ScriptRenderMode() const
    {
        if (Material.IsCullFace && !Material.IsAddColor && Material.IsUseDepth && !Material.IsWireframe) return "";
        QString format = "scene.renderMode('%1',%2,%3,%4,%5)";
        QString faceMode = Material.IsCullFace ? "scene.FACE_FRONT" : "scene.FACE_DOUBLE";
        QString blendMode = Material.IsAddColor ? "scene.BLEND_ADDITIVE" : "scene.BLEND_NORMAL";
        return QString(format).arg(Name, faceMode, blendMode, Material.IsUseDepth ? "true" : "false", Material.IsWireframe ? "true" : "false");
    }

    /**
     * @brief 物理绑定脚本
     * @return
     */
    QString ScriptPhysical() const
    {
        if (!Physical.Enable) return "";
        QString shapeScript = "";
        QString shapeName = QString("__shape_%1").arg(Name);
        if (!Physical.ShapeList.isEmpty())
        {
            QStringList scriptList;
            scriptList.append("local " + shapeName + " = {}");
            for (qsizetype i = 1; i <= Physical.ShapeList.size(); i++)
            {
                const QString geoName = Physical.ShapeList[i - 1];
                const QString itemName = QString("%1[%2]").arg(shapeName).arg(i);
                scriptList.append(QString("%1 = scene.handle('%2','GetGeometry',{worldspace=true})").arg(itemName, geoName));
                if (geoName != Name)
                {
                    scriptList.append(QString("%1['position_x'],%1['position_y'],%1['position_z'] = scene.position('%2',true)").arg(itemName, geoName));
                    scriptList.append(QString("%1['orientation_x'],%1['orientation_y'],%1['orientation_z'],%1['orientation_w'] = scene.orientation('%2',true)").arg(itemName, geoName));
                }
            }
            scriptList.append("");
            shapeScript = scriptList.join('\n');
        }
        QString format = "physics.bind('%1',{%2})";
        QString attrib = QString("mass=%1,shape=%2,linearDamping=%3,angularDamping=%4,friction=%5,restitution=%6,trigger=%7,allowSleep=%8")
                             .arg(Physical.Mass).arg(Physical.ShapeList.isEmpty() ? "nil" : shapeName)
                             .arg(Physical.LinearDamping).arg(Physical.AngularDamping)
                             .arg(Physical.Friction).arg(Physical.Restitution)
                             .arg(Physical.Trigger ? "true" : "false", Physical.AllowSleep ? "true" : "false");
        return shapeScript + QString(format).arg(Name, attrib);
    }

    /**
     * @brief 材质脚本
     * @return
     */
    QString ScriptMaterial() const
    {
        if (!Material.Enable) return "";
        QString format = "scene.material('%1',%2,{roughness=%3,metalness=%4,color='%5',emissive=%6})";
        return QString(format).arg(Name).arg(-1).arg(Material.Roughness).arg(Material.Metalness).arg(Material.Color).arg(Material.Emissive);
    }

    /**
     * @brief 构造函数
     * @param parent
     * @param type
     * @param name
     * @param attribute
     */
    EntitySceneNode(EntitySceneNode* parent, const QString& type, const QString& name, const QString& attribute)
    {
        Parent = parent;
        Type = type;
        Name = name;
        Attribute = attribute;
        Position = Vector3(0.0f, 0.0f, 0.0f);
        Angle = Vector3(0.0f, 0.0f, 0.0f);
        Scale = Vector3(1.0f, 1.0f, 1.0f);
        Visible = true;
    }

    /**
     * @brief 克隆对象
     * @return
     */
    EntitySceneNode* Clone()
    {
        int count = 1;
        QString name = this->Name + "_copy";
        while (Program::SceneNodeList.contains(name)) name = this->Name + "_copy" + QString::number(count++);
        EntitySceneNode* obj = new EntitySceneNode(this->Parent, this->Type, name, this->Attribute);
        obj->Position = this->Position;
        obj->Angle = this->Angle;
        obj->Scale = this->Scale;
        obj->Physical.Enable = this->Physical.Enable;
        obj->Physical.Trigger = this->Physical.Trigger;
        obj->Physical.AllowSleep = this->Physical.AllowSleep;
        obj->Physical.ShapeList = this->Physical.ShapeList;
        obj->Physical.Mass = this->Physical.Mass;
        obj->Physical.LinearDamping = this->Physical.LinearDamping;
        obj->Physical.AngularDamping = this->Physical.AngularDamping;
        obj->Physical.Friction = this->Physical.Friction;
        obj->Physical.Restitution = this->Physical.Restitution;
        obj->Material.Enable = this->Material.Enable;
        obj->Material.Roughness = this->Material.Roughness;
        obj->Material.Metalness = this->Material.Metalness;
        obj->Material.Color = this->Material.Color;
        obj->Material.Emissive = this->Material.Emissive;
        obj->Material.IsCullFace = this->Material.IsCullFace;
        obj->Material.IsAddColor = this->Material.IsAddColor;
        obj->Material.IsUseDepth = this->Material.IsUseDepth;
        obj->Material.IsWireframe = this->Material.IsWireframe;
        return obj;
    }

    /**
     * @brief 重载 ToString
     * @return
     */
    QString ToString() const
    {
        return Name;
    }
};

#endif // ENTITYSCENENODE_H
